#include <gtk/gtk.h>
#include <glade/glade.h>
#include "gtkwire_app.h"
#include "gtkwire_databank.h"
#include "gtkwire_utils.h"
#include "gtkwire_treemodel.h"


/* Constructor function prototype */
typedef GtkWidget* (*ConstructorFunc) 		();

/* GObject constructor function prototype */
typedef GObject*  (*GObjConstructorFunc) 		();

static GtkWidget* 		create_with_data 		(int hash_id, gpointer data);
static GtkWidget* 		file_chooser_button_create	(gpointer data);
static void 			wtable_destroy_func		(gpointer value);
static GObject* 		create_gobject_with_data 	(int type_id, gpointer data);
static GObject*			GtkTreeViewColumn_create	(gpointer data);
static GObject* 		GtkListStore_create 		(gpointer data);
static GObject*			GdkPixBuf_create 		(gpointer data);
static GObject*			GtkAdjustment_create 		(gpointer data);

/* Table for widgets, key = name */
static GHashTable *widget_table = NULL;

/* Table for widget creator functions, key = type string */
static GHashTable *widget_constr_table = NULL;

/* Table for G_Objects */
static GHashTable *gobjects_table = NULL;

/* Table for G_Object constructors */
static GHashTable *gobject_constr_table = NULL;

/* Table for tree irews_Objects 
static GHashTable *tree_iter_table = NULL;
static int next_tree_iter_id = 6;
*/
static gsize INT_SIZE =  sizeof(int);


void 
init_bank ()
{
	/* Create hashtable for created widgets */
	widget_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, wtable_destroy_func);

	/* Create hashtable for no parameter constructors */
	widget_constr_table = g_hash_table_new (g_str_hash, g_str_equal);

	/* Create table for G_Objects */
	gobjects_table =  g_hash_table_new (g_int_hash, g_int_equal);

	/* Create table for G_Object constructors */
	gobject_constr_table = g_hash_table_new (g_int_hash, g_int_equal);

	//tree_iter_table = g_hash_table_new (g_int_hash, g_int_equal);

	/* Fill widget constructor table */
	g_hash_table_insert (widget_constr_table, "GtkButton", gtk_button_new);
	g_hash_table_insert (widget_constr_table, "GtkColorButton", gtk_color_button_new);
	g_hash_table_insert (widget_constr_table, "GtkDialog", gtk_dialog_new);
	g_hash_table_insert (widget_constr_table, "GtkAboutDialog", gtk_about_dialog_new);
	g_hash_table_insert (widget_constr_table, "GtkAssistant", gtk_assistant_new);
	g_hash_table_insert (widget_constr_table, "GtkToggleButton", gtk_toggle_button_new);
	g_hash_table_insert (widget_constr_table, "GtkCheckButton", gtk_check_button_new);
	g_hash_table_insert (widget_constr_table, "GtkFontButton", gtk_font_button_new);
	g_hash_table_insert (widget_constr_table, "GtkOptionMenu",  gtk_option_menu_new);
	//g_hash_table_insert (widget_constr_table, "GtkVolumeButton", gtk_volume_button_new); 2.12
	g_hash_table_insert (widget_constr_table, "GtkSeparatorMenuItem", gtk_separator_menu_item_new);
	g_hash_table_insert (widget_constr_table, "GtkComboBox", gtk_combo_box_new_text);
	g_hash_table_insert (widget_constr_table, "GtkComboBoxEntry", gtk_combo_box_entry_new);
	g_hash_table_insert (widget_constr_table, "GtkEventBox", gtk_event_box_new);
	g_hash_table_insert (widget_constr_table, "GtkHandleBox", gtk_handle_box_new);
	g_hash_table_insert (widget_constr_table, "GtkToolItem", gtk_tool_item_new);
	g_hash_table_insert (widget_constr_table, "GtkSeparatorToolItem", gtk_separator_tool_item_new);
	g_hash_table_insert (widget_constr_table, "GtkHButtonBox", gtk_hbutton_box_new);
	g_hash_table_insert (widget_constr_table, "GtkVButtonBox", gtk_vbutton_box_new);
	g_hash_table_insert (widget_constr_table, "GtkColorSelection", gtk_color_selection_new);
	g_hash_table_insert (widget_constr_table, "GtkFontSelection", gtk_font_selection_new);
	g_hash_table_insert (widget_constr_table, "GtkRecentChooserWidget", gtk_recent_chooser_widget_new);
	g_hash_table_insert (widget_constr_table, "GtkGammaCurve", gtk_gamma_curve_new);
	g_hash_table_insert (widget_constr_table, "GtkCombo", gtk_combo_new);
	g_hash_table_insert (widget_constr_table, "GtkStatusbar", gtk_statusbar_new);
	g_hash_table_insert (widget_constr_table, "GtkHPaned", gtk_hpaned_new);
	g_hash_table_insert (widget_constr_table, "GtkVPaned", gtk_vpaned_new);
	g_hash_table_insert (widget_constr_table, "GtkIconView", gtk_icon_view_new);
	g_hash_table_insert (widget_constr_table, "GtkMenuBar", gtk_menu_bar_new);
	g_hash_table_insert (widget_constr_table, "GtkMenu",gtk_menu_new);
	g_hash_table_insert (widget_constr_table, "GtkRecentChooserMenu", gtk_recent_chooser_menu_new);
	g_hash_table_insert (widget_constr_table, "GtkNotebook", gtk_notebook_new);
	g_hash_table_insert (widget_constr_table, "GtkTextView", gtk_text_view_new);
	g_hash_table_insert (widget_constr_table, "GtkToolbar", gtk_toolbar_new);
	g_hash_table_insert (widget_constr_table, "GtkTreeView", gtk_tree_view_new);
	g_hash_table_insert (widget_constr_table, "GtkCalendar", gtk_calendar_new);
	g_hash_table_insert (widget_constr_table, "GtkCellView", gtk_cell_view_new);
	g_hash_table_insert (widget_constr_table, "GtkDrawingArea", gtk_drawing_area_new);
	g_hash_table_insert (widget_constr_table, "GtkEntry", gtk_entry_new);
	g_hash_table_insert (widget_constr_table, "GtkHRuler", gtk_hruler_new);
	g_hash_table_insert (widget_constr_table, "GtkHRuler", gtk_vruler_new);
	g_hash_table_insert (widget_constr_table, "GtkVSeparator", gtk_vseparator_new);
	g_hash_table_insert (widget_constr_table, "GtkHSeparator", gtk_hseparator_new);
	g_hash_table_insert (widget_constr_table, "GtkProgressBar", gtk_progress_bar_new);
   	g_hash_table_insert (widget_constr_table, "GtkDrawingArea", gtk_drawing_area_new);
   	g_hash_table_insert (widget_constr_table, "GtkTreeView", gtk_tree_view_new);
   	g_hash_table_insert (widget_constr_table, "GtkHSeparator", gtk_hseparator_new);

	/* Fill GObject constructors table. */
	gpointer key1;
	key1 = int_key (GO_GtkCellRendererText);
     	g_hash_table_insert (gobject_constr_table, key1, gtk_cell_renderer_text_new);

	gpointer key2;
	key2 = int_key (GO_GtkCellRendererToggle);
     	g_hash_table_insert (gobject_constr_table, key2, gtk_cell_renderer_toggle_new);

	gpointer key3;
	key3 = int_key (GO_GtkCellRendererStockImage);
     	g_hash_table_insert (gobject_constr_table, key3,  gtk_cell_renderer_pixbuf_new);

	gpointer key4;
	key4 = int_key (GO_GtkCellRendererProgress);
     	g_hash_table_insert (gobject_constr_table, key4,  gtk_cell_renderer_progress_new);

	gpointer key5;
	key3 = int_key (GO_GtkCellRendererPixbuf);
     	g_hash_table_insert (gobject_constr_table, key3,  gtk_cell_renderer_pixbuf_new);
}

/* create dunamic object to be used as a has key */
gpointer
int_key (int key_val)
{
	int *ip;
	ip = g_malloc (INT_SIZE);
	*ip = key_val;
	return ip;
} 

void
create_widget (const char *type_id, const char *name, int hash_id, gpointer data)
{
	GtkWidget *widget;
	ConstructorFunc constr_func = g_hash_table_lookup (widget_constr_table, type_id);

	if (constr_func == NULL )
		//--- Some widgets need info straight into constructor, so don't fit typedef 
		widget = create_with_data (hash_id, data);
	else
		//--- Usual case
		widget = constr_func ();

	if (widget == NULL )
	{
		printf("widget creation failed for:");
		printf(type_id);
		//--- some java notification?
		return;
	}

	/* Set name */
	gtk_widget_set_name (widget, name);
	
	/* Widget created, put it into hashtable for later access */
	g_hash_table_insert (widget_table, g_strdup (name), widget);

	/* to be removed */
	const gchar* widget_type = g_type_name ( G_OBJECT_TYPE(widget) );
	const gchar* widget_name = gtk_widget_get_name ( widget );
	printf( widget_type );
	printf( "," );
	printf( widget_name );
	printf( "\n" );
}

/* Creates widgets that must get some data in constructor */
static GtkWidget* 
create_with_data (int hash_id, gpointer data)
{
	switch (hash_id)
	{
		case GtkWindow_ID:
			return  gtk_window_new (get_gtk_window_type(data));

		case GtkVBox_ID:
			return gtk_vbox_new (TRUE, 0);

		case GtkCairoDrawable_ID:
			/* From GTK+ point of view GtkCairoDrawable is actually GtkDrawingArea,
			* gtkwire just adds functionality and calls it different.
			*/
			return gtk_drawing_area_new ();

		case GtkFrame_ID:
			return gtk_frame_new (read_str_from_glist(data,0));

		case GtkAlignment_ID:
			return gtk_alignment_new (0.5, 0.5, 1.0, 1.0);

		case GtkFileChooserButton_ID:
			return gtk_file_chooser_button_new (read_str_from_glist(data,0),
								get_gtk_file_chooser_mode( read_str_from_glist(data,1)));
		case GtkTable_ID:
			return gtk_table_new (	atoi(read_str_from_glist(data,0)),
						atoi(read_str_from_glist(data,1)),
						str_to_boolean(read_str_from_glist(data,2)) );
		case GtkLabel_ID:
			return gtk_label_new (NULL);

		case GtkHScale_ID:
			return gtk_hscale_new (GTK_ADJUSTMENT (get_g_object(atoi(read_str_from_glist(data,0)))));

		case GtkExpander_ID:
			return gtk_expander_new (read_str_from_glist(data, 0));

		case GtkAspectFrame_ID:
			return gtk_aspect_frame_new (	read_str_from_glist(data, 0),
							atof (read_str_from_glist(data, 1)),
							atof (read_str_from_glist(data, 2)),
							atof (read_str_from_glist(data, 3)),
							str_to_boolean (read_str_from_glist(data, 4)) );
		case GtkFontSelectionDialog_ID:
			return gtk_font_selection_dialog_new (read_str_from_glist(data, 0));

		case GtkFileChooserDialog_ID:
			return gtk_file_chooser_dialog_new (	read_str_from_glist(data, 0),
                                                         	GTK_WINDOW (get_widget (read_str_from_glist(data, 1))),
                                                         	get_gtk_file_chooser_mode (read_str_from_glist(data, 1)),
                  	                                     	NULL );
		case GtkMenuItem_ID:
			return gtk_menu_item_new_with_label (read_str_from_glist(data, 0));

		case GtkImageMenuItem_ID:
			return gtk_image_menu_item_new_with_label (read_str_from_glist(data, 0));

		case GtkSpinButton_ID:
			return gtk_spin_button_new_with_range (atof(read_str_from_glist(data, 0)),
								atof(read_str_from_glist(data, 1)),
								atof(read_str_from_glist(data, 2)));
		case GtkToolButton_ID:
			return GTK_WIDGET(gtk_tool_button_new (get_widget (read_str_from_glist(data, 1)), read_str_from_glist(data, 0)));

		case GtkColorSelectionDialog_ID:
			return gtk_color_selection_dialog_new (read_str_from_glist(data, 0));

		default:
			printf ("widget create_with_data default hit\n" );
			return NULL;
	}

	return NULL;
}

/*
* If for some reason duplicate named widget is creted this will destroy them.
*/
static void 
wtable_destroy_func (gpointer value)
{
	 gtk_widget_destroy (GTK_WIDGET(value));
}

//-------------------------------------------------------------------------------- SPECIAL WIDGET CREATE METHODS
static GtkWidget* 
file_chooser_button_create (gpointer data)
{

	GtkWidget *button;
	button = gtk_file_chooser_button_new (read_str_from_glist(data,0),
							GTK_FILE_CHOOSER_ACTION_OPEN );
	gtk_file_chooser_set_filename       (GTK_FILE_CHOOSER(button), read_str_from_glist(data,1));
	return button;
}

//--------------------------------------------------------------------------------- GET WIDGET
GtkWidget*
get_widget (const char *widget_name)
{
	GtkWidget *widget;
	/* First look if can get from glade */
	widget = get_glade_widget (widget_name);
	/* Then get a created a widget */
	if (widget == NULL) widget = g_hash_table_lookup (widget_table, widget_name);
	return widget;
}

/* not impl */
void
destroy_widget_from_bank (const char *widget_name)
{

}

void 
insert_widget (GtkWidget *widget, const char *widget_name)
{
	g_hash_table_insert (widget_table, g_strdup (widget_name), widget);
}

//------------------------------------------------------------------------------------- G OBJECTS
void
create_new_g_object (int type_id, int id, gpointer data)
{
	GObject *object;
	gpointer key = int_key (type_id);
	GObjConstructorFunc g_constr_func = g_hash_table_lookup (gobject_constr_table, key);
	g_free(key);
	if (g_constr_func == NULL)
		object = create_gobject_with_data (type_id, data);

	else
		object = g_constr_func ();

	if (object == NULL ) printf("object null");//error handling?
	gpointer hash_key;
	hash_key = int_key (id);
	g_hash_table_insert (gobjects_table, hash_key, object);
}

static GObject* 
create_gobject_with_data (int type_id, gpointer data)
{
	switch (type_id)
	{
		case GO_GtkListStore:
			return GtkListStore_create (data);

		case GO_GtkTreeViewColumn:
			return GtkTreeViewColumn_create (data);

		case GO_GdkPixBuf:
			return GdkPixBuf_create (data);

		case GO_GtkAdjustment:
			return GtkAdjustment_create (data);
	}
	printf("create_gobject_with_data NULL ret\n");
	return NULL;
}

/* GObjecte crete methods */
static GObject*
GtkTreeViewColumn_create (gpointer data)
{
	GtkTreeViewColumn *tree_column;
	tree_column = gtk_tree_view_column_new ();
	GList *list = data;

	GtkCellRenderer *renderer;
	renderer = GTK_CELL_RENDERER (get_g_object (atoi( read_str_from_glist(list, 1))));

	gtk_tree_view_column_set_title (tree_column, read_str_from_glist(list, 0) );
	gtk_tree_view_column_pack_start (tree_column, renderer, TRUE);
	gtk_tree_view_column_add_attribute (	tree_column,
						renderer,
						read_str_from_glist(list, 3),
						atoi(read_str_from_glist(list, 2)));
	return G_OBJECT (tree_column);
}

static GObject*
GtkListStore_create (gpointer data)
{
	GList *list = data;
	int len = g_list_length (list);
	GType cTypes [len];
	int i;
	for (i = 0; i < len; i++)
	{
		cTypes[i] = get_gtype (atoi(list->data));
		list = list->next;
	}

	return G_OBJECT (gtk_list_store_newv (len, cTypes));
}

static GObject*
GdkPixBuf_create (gpointer data)
{
	GError *error = NULL;
	GdkPixbuf    *pixbuf;

	GList *list = data;
	const char *file = read_str_from_glist(list, 0);
	
	pixbuf = gdk_pixbuf_new_from_file(file, &error);

	if (error)
	{
		g_critical ("Could not load pixbuf: %s\n", error->message);
		g_error_free(error);
		return;
	}

	return G_OBJECT (pixbuf);
}

static GObject*
GtkAdjustment_create (gpointer data)
{
	double *msg = data;
	return G_OBJECT (gtk_adjustment_new (msg[1], msg[2], msg[3], msg[4], msg[5], msg[6]));
}

void
destroy_g_object (int id)
{

}

GObject*
get_g_object (int id)
{
	GObject *object;

	gpointer key = int_key (id);
	object = g_hash_table_lookup (gobjects_table, key);
	g_free(key);

	return object;
}



/* Text iter handling 
int
save_iter (GtkTreeIter *iter)
{
	int newid = next_tree_iter_id;
	gpointer key = int_key (newid);
	g_hash_table_insert (tree_iter_table, key, iter);
	g_free(key);
	next_tree_iter_id++;
	return newid;
}

GtkTreeIter*
get_iter (int iter_id)
{
	GtkTreeIter *iter;

	gpointer key = int_key (iter_id);
	iter = g_hash_table_lookup (gobjects_table, key);
	g_free(key);
	return iter;
}
*/
